package cn.liberg.coder.tool.template;

import cn.liberg.coder.tool.LibergToolContext;
import cn.liberg.coder.tool.LibergToolException;
import cn.liberg.coder.tool.core.Formats;
import cn.liberg.coder.tool.java.*;

import java.util.*;

/**
 * 生成data.dao.impl.XxxDaoImpl.java文件
 * 此文件完全由本代码插件生成和覆盖，
 * 生成的java代码文件不应该被手动修改
 */
public class TempDaoImpl {
    public static final String SUFFIX = "DaoImpl";

    LibergToolContext context;
    String entityName;
    String selfName;
    String tableName;
    String selfPath;
    JClass parser;
    JClassEntity entity;
    List<String> cachedColumns = new ArrayList<>();
    CacheGroupManager cgm = new CacheGroupManager();

    public static final Map<String, String> UPPER_TYPE_MAP = new HashMap<>();

    static {
        UPPER_TYPE_MAP.put("String", "String");
        UPPER_TYPE_MAP.put("int", "Integer");
        UPPER_TYPE_MAP.put("Integer", "Integer");
        UPPER_TYPE_MAP.put("byte", "Byte");
        UPPER_TYPE_MAP.put("Byte", "Byte");
        UPPER_TYPE_MAP.put("long", "Long");
        UPPER_TYPE_MAP.put("Long", "Long");
    }

    public TempDaoImpl(LibergToolContext ctx, JClassEntity entity) throws LibergToolException {
        context = ctx;
        this.entity = entity;
        entityName = entity.name;
        tableName = Formats.toTableName(entityName);
        selfName = entityName + SUFFIX;
        selfPath = context.getDaoImplPath() + selfName + ".java";

        //每次都是覆盖，所以不需要读取旧的文件内容
        parser = new JClass(selfPath, false);
        initTemplate();
    }

    private String getPackage() {
        return context.getDaoImplPackage();
    }

    public void save() throws LibergToolException {
        parser.writeToFile(selfPath);
        String tip = "  created/overwritten.";
        System.out.println("> " + getPackage() + "." + selfName + tip);
    }

    private void initTemplate() throws LibergToolException {
        parser.classDescs = Arrays.asList(JDesc.selfGeneratedDesc());
        parser.mPackage = getPackage();
        parser.addImport(context.getEntityPackage() + "." + entityName);
        parser.addImport("cn.liberg.core.*");
        parser.addImport("cn.liberg.database.BaseDao");
        parser.addImport(context.getEntityPackage() + "." + entityName);

        parser.addImport("java.sql.PreparedStatement");
        parser.addImport("java.sql.ResultSet");
        parser.addImport("java.sql.SQLException");
        parser.addImport("java.util.ArrayList");
        parser.addImport("java.util.Arrays");
        parser.addImport("java.util.List");

        parser.name = selfName;
        parser.defLine = "public class " + selfName + " extends BaseDao<" + entityName + "> {";
        initBody();
    }

    private int promoteSize(int size) {
        int curr = 4;
        while (curr < size) {
            curr <<= 1;
        }
        return curr;
    }

    private void initBody() throws LibergToolException {
    	// XxxDaoImpl中的构造方法
        JMethod constructor = new JMethod("protected " + selfName + "() {");

		//buildEntity(ResultSet rs)方法
        JMethod jm_buildEntity = new JMethod("public final " + entityName + " buildEntity(ResultSet rs) throws SQLException {");
        jm_buildEntity.addAnnoLine("@Override");
        jm_buildEntity.appendBodyLine("        " + entityName + " entity = new " + entityName + "();");
        jm_buildEntity.appendBodyLine("        entity.id = rs.getLong(1);");
		//fillPreparedStatement方法
        JMethod jm_fillPreparedStatement = new JMethod("protected final void fillPreparedStatement(" + entityName + " entity, PreparedStatement ps) throws SQLException {");
        jm_fillPreparedStatement.addAnnoLine("@Override");

        // getIdColumn()
        JMethod jm_getIdColumn = new JMethod("public Column<"+entityName+", Long> getIdColumn() {");
        jm_getIdColumn.addAnnoLine("@Override");
        jm_getIdColumn.appendBodyLine("        return columnId;");

		//initColumns()
        JMethod jm_initColumns = new JMethod("public final List<Column> initColumns() {");
        jm_initColumns.addAnnoLine("@Override");
        jm_initColumns.appendBodyLine("        List<Column> columns = new ArrayList<>(" + promoteSize(entity.getFields().size() - 1) + ");");

        parser.addField(new JField("public static final String TABLE_NAME = \"" + tableName + "\";"));
        String varName;
        String upperType;
        String upperFirstLetterType;
        String shortName;
        MetaAnno anno;
        String t1, t2;
        String args;
        boolean isCached = false;//列上是否开启了缓存
        int index = 0;
        Collection<JField> jFields = entity.getFields().values();
        for (JField jf : jFields) {
            anno = jf.getAnno();
            // @dbmap(isMap=false)的成员不映射到数据表的列
            if (anno.hasFalse("dbmap", "isMap")) {
                continue;
            }
            isCached = anno.has("cache");
            // 被"号包裹的短名称
            shortName = anno.getValue("JSONField", "name");
            upperType = UPPER_TYPE_MAP.get(jf.type);
            upperFirstLetterType = Formats.upperEntityField(jf.type);
            varName = Formats.toColumnFieldName(jf.name);

            CacheAnno cacheAnno = null;
            if (isCached) {
                cacheAnno = CacheAnno.from(anno);
                if(cacheAnno.capGt0()) {
                    cachedColumns.add(varName);
                }
                if(cacheAnno.group.length() > 0) {
					CacheField cf = new CacheField(jf.name, upperType, cacheAnno.groupCap, cacheAnno.seq);
					cgm.addField(cacheAnno.group, cf);
				}
            }

            t1 = isCached ? "CachedColumn" : "Column";
            //先处理id列
            if (Formats.ID.equals(jf.name)) {
                t2 = isCached ? "CachedIdColumn" : "IdColumn";
                args = isCached ? cacheAnno.cap : "";
                appendColumn(upperType, jf.name, t1, t2, args);
                continue;
            }
            //处理剩余的列
            args = "\"" + jf.name + "\", " + shortName;
            t2 = upperType + "Column";
            if (isCached) {
                t2 = "Cached" + t2;
                args += ", " + cacheAnno.cap;
            }
            appendColumn(upperType, jf.name, t1, t2, args);

            index++;
            jm_buildEntity.appendBodyLine("        entity." + jf.name + " = rs.get" + upperFirstLetterType + "(" + (index + 1) + ");");
            jm_fillPreparedStatement.appendBodyLine("        ps.set" + upperFirstLetterType + "(" + index + ", entity." + jf.name + ");");
            jm_initColumns.appendBodyLine("        columns.add(" + varName + ");");
        }

        appendColumnGroup();
        if(cachedColumns.size() > 0) {
            constructor.appendBodyLine("        super(TABLE_NAME, Arrays.asList(");
            for (int i = 0; i < cachedColumns.size(); i++) {
                String end = i < cachedColumns.size() - 1 ? "," : "";
                constructor.appendBodyLine("                " + cachedColumns.get(i) + end);
            }
            constructor.appendBodyLine("        ));");
        } else {
            constructor.appendBodyLine("        super(TABLE_NAME, null);");
        }

        jm_buildEntity.appendBodyLine("        return entity;");
        jm_initColumns.appendBodyLine("        return columns;");

        parser.addMethod(constructor);
        parser.addMethod(makeJMethod_getEntityClazz());
        parser.addMethod(jm_buildEntity);
        parser.addMethod(jm_fillPreparedStatement);
        parser.addMethod(jm_getIdColumn);
        parser.addMethod(jm_initColumns);
    }

    private JMethod makeJMethod_getEntityClazz() {
        JMethod jm = new JMethod("public Class<"+entityName+"> getEntityClazz() {");
        jm.addAnnoLine("@Override");
        jm.appendBodyLine("        return "+entityName+".class;");
        return jm;
    }

    private void appendColumnGroup() throws LibergToolException {
        List<CacheGroup> groups = cgm.getGroups();
        for (CacheGroup group : groups) {
            String line = "public static final " + group.type + "<" + entityName + ", " + group.typeArgs + "> "
                    + group.columnName + " = new " + group.type + "(" + group.callArgs + ");";
            JField jf = new JField(line);
            parser.addField(jf);
            cachedColumns.add(group.columnName);
        }
    }

    private void appendColumn(String fieldType, String fieldName, String t1, String t2, String args) {
        String varName = Formats.toColumnFieldName(fieldName);
        t1 = t1 + "<" + entityName + ", " + fieldType + ">";
        t2 = t2 + "<" + entityName + ">";
        JField jf = new JField("public static final " + t1 + " " + varName + " = new " + t2 + "(" + args + ") {");
        jf.addLine("        @Override");
        jf.addLine("        public void set(" + entityName + " entity, " + fieldType + " value) {");
        jf.addLine("            entity." + fieldName + " = value;");
        jf.addLine("        }");
        jf.addLine("        @Override");
        jf.addLine("        public " + fieldType + " get(" + entityName + " entity) {");
        jf.addLine("            return entity." + fieldName + ";");
        jf.addLine("        }");
        jf.addLine("    };");
        parser.addField(jf);
    }
}

